// 用于开发时在本地运行的web服务程序

const LRU = require('lru-cache')
//const favicon = require('serve-favicon')
//const compression = require('compression')
//const microcache = require('route-cache')
const Koa = require('koa')
const server = new Koa()
const { createBundleRenderer } = require('vue-server-renderer')
const fs = require('fs')
const path = require('path')
const resolve = file => path.resolve(__dirname, file)
const templatePath = resolve('./src/index.template.html')
let template = fs.readFileSync(templatePath, 'utf-8')
const mfs = new (require('memory-fs'))()
const chokidar = require('chokidar')
const webpack = require('webpack')
const clientConfig = require('./build/webpack.client.config')
const serverConfig = require('./build/webpack.server.config')
let renderer, bundle, clientManifest

// 模块热替换相关处理
clientConfig.entry.app = ['webpack-hot-middleware/client', clientConfig.entry.app]
clientConfig.output.filename = '[name].js'
clientConfig.plugins.push(
  new webpack.HotModuleReplacementPlugin(),
  new webpack.NoEmitOnErrorsPlugin()
)

// 读取webpack输出文件
const readFile = (fs, file) => {
  try {
    return fs.readFileSync(path.join(clientConfig.output.path, file), 'utf-8')
  } catch (e) { }
}

// 处理webpack错误，返回值Boolean，表示是否有错误
const handleWebpackError = (err, stats) => {
  if (err) {
    console.error(err.stack || err);
    if (err.details) {
      console.error(err.details);
    }
    return true
  }
  const info = stats.toJson();
  if (stats.hasErrors()) {
    console.error(info.errors);
    return true
  }
  if (stats.hasWarnings()) {
    console.warn(info.warnings);
  }
  return false
}

// 更新renderer
const updateRenderer = () => {
  if (bundle && clientManifest && template) {
    renderer = createBundleRenderer(bundle, {
      template,
      clientManifest,
      // for component caching
      cache: LRU({
        max: 1000,
        maxAge: 1000 * 60 * 15
      }),
      // this is only needed when vue-server-renderer is npm-linked
      basedir: resolve('./dist'),
      // recommended for performance
      runInNewContext: false
    })
    return renderer
  }
  return false
}

// 启动渲染vue实例的服务器
const startServer = () => {
  if (!updateRenderer()) return

  server.use(async ctx => {
    let s = Date.now()
    const context = {
      title: '娱乐猫',
      url: ctx.url
    }
    renderer.renderToString({
      title: '娱乐猫',
      url: ctx.url
    }).then(html => {
      ctx.body = html
      console.log(`whole request: ${Date.now() - s}ms`)
    }).catch(err => console.error(err))
  })

  server.listen(8080, () => console.log('Server listening on: http://localhost:8080'))
}

// bundle相关处理
/* const serverCompiler = webpack(serverConfig)
serverCompiler.outputFileSystem = mfs
serverCompiler.run((err, stats) => {
  if (handleWebpackError(err, stats)) return
  bundle = JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json'))

  // 模块热替换相关处理
  clientConfig.entry.app = ['webpack-hot-middleware/client', clientConfig.entry.app]
  clientConfig.output.filename = '[name].js'
  clientConfig.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin()
  )

  // clientManifest相关处理
  const clientCompiler = webpack(clientConfig)
  console.log(serverCompiler)
  console.log(clientCompiler)
  return;
  const devMiddleware = require('koa-webpack-dev-middleware')(clientCompiler)
  server.use(devMiddleware)
  const hotMiddleware = require('koa-webpack-hot-middleware')(clientCompiler, { heartbeat: 5000 })
  server.use(hotMiddleware)
  clientCompiler.watch({}, (err, stats) => {
    if (handleWebpackError(err, stats)) return
    clientManifest = JSON.parse(readFile(
      devMiddleware.fileSystem,
      'vue-ssr-client-manifest.json'
    ))
    startServer()
  })
}) */

const compiler = webpack([serverConfig, clientConfig])
compiler.outputFileSystem = mfs
const devMiddleware = require('koa-webpack-dev-middleware')(compiler)
server.use(devMiddleware)
const hotMiddleware = require('koa-webpack-hot-middleware')(compiler, { heartbeat: 5000 })
server.use(hotMiddleware)
compiler.watch({}, (err, stats) => {
  if (handleWebpackError(err, stats)) return
  bundle = JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json'))
  clientManifest = JSON.parse(readFile(
    devMiddleware.fileSystem,
    'vue-ssr-client-manifest.json'
  ))
  startServer()
})

// template相关处理
chokidar.watch(templatePath).on('change', () => {
  template = fs.readFileSync(templatePath, 'utf-8')
  console.log('index.html template updated.')
  updateRenderer()
})